# 从Django的分页模块中导入分页器和相关异常类
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
# 导入用于渲染模板和获取对象或返回404错误的快捷函数
from django.shortcuts import render, get_object_or_404
# 导入发送邮件的函数
from django.core.mail import send_mail
# 导入Post模型，该模型代表博客文章
from .models import Post
# 导入自定义表单类，用于发送邮件和提交评论
from .forms import EmailPostForm, CommentForm
# 导入Taggit的Tag模型，用于处理文章标签
from taggit.models import Tag
# 导入Django的数据库操作函数，用于执行复杂的查询，如计数
from django.db.models import Count
# 导入 Django 提供的搜索向量类
from django.contrib.postgres.search import SearchVector
# 从本地表单模块导入所需的表单类
from .forms import EmailPostForm, CommentForm, SearchForm

 
 
# Create your views here.
 
# 文章列表视图函数，支持按标签过滤
def post_list(request, tag_slug=None):
    # 获取所有已发布的文章
    posts = Post.published.all()
    # 创建变量 tag，用于存储下方代码获取的标签
    tag = None
    # 如果请求中有 tag_slug，则获取标签并按标签过滤文章数据
    if tag_slug: 
        # 获取标签
        tag = get_object_or_404(Tag, slug=tag_slug)
        # 按标签过滤文章
        posts = posts.filter(tags=tag)
    # 创建分页器，每页显示3篇文章
    paginator = Paginator(posts, 3)
    # 获取请求参数中的页码
    page = request.GET.get('page')
     
    try:
        # 尝试获取对应页码的文章
        posts = paginator.page(page)
    except PageNotAnInteger:
        # 如果页码不是整数，则显示第一页
        posts = paginator.page(1)
    except EmptyPage:
        # 如果页码超出范围，则显示最后一页
        posts = paginator.page(paginator.num_pages)
     
    # 渲染文章列表页面，并返回文章和标签上下文
    return render(request, 'blog/post/list.html', {'posts': posts, 'page': page,'tag': tag})
 
# 文章详情视图函数，显示文章详细信息及评论
def post_detail(request, year, month, day, post):
    post = get_object_or_404(Post, slug=post, status='published', publish__year=year, publish__month=month, publish__day=day)
    # 获取数据库中当前文章的所有评论中状态为可以查看的评论
    comments = post.comments.filter(active=True)
    # 创建一个新评论变量，默认为 None
    new_comment = None
    # 如果请求类型是 POST，则获取请求中的评论数据构建评论并保存到数据库
    if request.method == 'POST':
        # 获取请求中的评论数据构建评论表单
        comment_form = CommentForm(data=request.POST)
        # 如果评论表单有效，保存新的评论
        if comment_form.is_valid():
            # 创建新的评论实例但不保存到数据库
            new_comment = comment_form.save(commit=False)
            # 将该评论关联到特定的帖子
            new_comment.post = post
            # 保存评论到数据库
            new_comment.save()
    else:
        # 如果请求类型不是 POST，则构建空评论表单
        comment_form = CommentForm()
 
    # 找到所有相似文章
    # 找到当前文章的标签编号
    post_tags_ids = post.tags.values_list('id', flat=True)
    # 找到包含当前文章任意标签的文章集合（除当前文章外）
    similar_posts = Post.published.filter(tags__in=post_tags_ids)\
    .exclude(id=post.id)
    # 对相似文章按照相同标签数量进行排序，取前 4 个，相同标签数量使用聚合函数 Count
    # 计算后保存在 same_tags 中，以便后续使用
    similar_posts = similar_posts.annotate(same_tags=Count('tags'))\
    .order_by('-same_tags','-publish')[:4]
    
    # 传递并渲染所有评论和新评论数据至文章详情模板
    return render(request,
              'blog/post/detail.html', {
                  'post': post,
                  'comments': comments,
                  'new_comment': new_comment,
                  'comment_form': comment_form,
                  'similar_posts': similar_posts  # 添加这一行以传递相似文章数据
              })
 
# 文章分享视图函数，通过邮件分享文章
def post_share(request, post_id):
    # 处理分享博客文章的视图函数
    # 获取指定ID和状态为“published”的博客文章对象，如果找不到则返回404错误
    post = get_object_or_404(Post, id=post_id, status='published')
    sent = False
    
    # 检查请求方法是否为POST
    if request.method == 'POST':
        # 使用POST数据初始化表单
        form = EmailPostForm(request.POST)
        # 验证表单数据是否有效
        if form.is_valid():
            # 获取表单中的数据
            cd = form.cleaned_data
            # 构建文章的绝对URL
            post_url = request.build_absolute_uri(post.get_absolute_url())
            # 构造邮件主题
            subject = f"{cd['name']} recommends you read " \
                      f"{post.title}"
            # 构造邮件内容
            message = f"Read {post.title} at {post_url}\n\n" \
                      f"{cd['name']}'s comments: {cd['comments']}"
            # 发送邮件
            send_mail(subject, message, '3484181956@qq.com', [cd['to']])
            sent = True
        else:
            # 如果表单无效，重新初始化表单
            form = EmailPostForm()
    
    # 渲染分享页面，传递文章对象、表单和发送状态
    return render(request, 'blog/post/share.html', {'post': post,
                                                    'form': EmailPostForm(),
                                                    'sent': sent})

def post_search(request):
    # 创建一个空的搜索表单实例
    form = SearchForm()
    # 初始化搜索内容和搜索结果为空
    query = None
    results = []
    # 检查请求中是否包含 'query' 参数，即用户是否进行了搜索
    if 'query' in request.GET:
        # 使用请求中的数据创建一个新的搜索表单实例
        form = SearchForm(request.GET)
        # 检查表单数据是否有效
        if form.is_valid():
            # 获取用户输入的搜索内容
            query = form.cleaned_data['query']
            # 创建一个搜索向量，包含文章标题和正文
            # 并根据搜索内容筛选出匹配的文章
            results = Post.published.annotate(
                search=SearchVector('title', 'body')
            ).filter(search=query)
    # 将搜索表单、搜索内容和搜索结果渲染到搜索模板并返回
    return render(request,
                  'blog/post/search.html', {
                      'form': form,
                      'query': query,
                      'results': results
                  })

